package com.borened.common.boot.config;

import com.borened.common.boot.config.properties.ThreadPoolProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池执行器配置
 * <p>
 * 在此类的配置基础上，可以使用spring提供的@Async注解实现异步控制。
 * 例：@Async("taskExecutor")
 *    <ol>
 *        <li>
 *            异步方法使用注解@Async的返回值只能为void或者Future。
 *            void类型用于不关心异步操作的结果/Future用于实现异步回调的效果,一般是对多个任务异步处理，在主任务中等待结果再返回响应的场景（降低了整体响应时间）。
 *        </li>
 *        <li>void方法的异常由默认SimpleAsyncUncaughtExceptionHandler处理,主线程不受影响。 Future方法的异常会中断任务，主线程会受影响。</li>
 *        <li>像@Transactional和@Async注解的实现都是基于Spring的AOP，而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了，有可能因为调用方法的是对象本身而不是代理对象，因为没有经过Spring容器</li>
 *    </ol>
 * </p>
 *
 * @author: cch
 * @since: 2022/3/10
 */

@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
    /**
     * 默认的任务拒绝策略
     * CALLER_RUNS：不在新线程中执行任务，而是有调用者所在的线程来执行
     */
    public static final RejectedExecutionHandler DEFAULT_REJECTION_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();

    @Bean
    @ConditionalOnMissingBean
    public ThreadPoolProperties threadPoolProperties() {
        return new ThreadPoolProperties();
    }

    /**
     * 自定义线程池。覆盖Spring默认的异步任务处理线程池，可以直接使用@Async注解而不用指定线程池。
     */

    @Bean(name = "taskExecutor")
    @ConditionalOnMissingBean(name = {"taskExecutor"})
    public Executor taskExecutor(ThreadPoolProperties threadPoolProperties) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(threadPoolProperties.getCorePoolSize());
        //配置最大线程数
        executor.setMaxPoolSize(threadPoolProperties.getMaxPoolSize());
        //配置队列大小
        executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(threadPoolProperties.getThreadNamePrefix());

        //设置关机时等待线程执行完毕和最大等待时间
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        // rejection-policy拒绝策略：当pool已经达到max size的时候，如何处理新任务
        executor.setRejectedExecutionHandler(DEFAULT_REJECTION_POLICY);
        /**
         * 设置线程池装饰者，实现当前请求线程上下文对象的传递（例如请求对象传递采参数）。
         */
        executor.setTaskDecorator(runnable -> {
            //非web请求线程,不包装
            RequestAttributes context = null;
            try {
                context = RequestContextHolder.currentRequestAttributes();
            } catch (IllegalStateException e) {
                return runnable;
            }
            //web请求线程
            RequestAttributes finalContext = context;
            return () -> {
                try {
                    RequestContextHolder.setRequestAttributes(finalContext, true);
                    runnable.run();
                } finally {
                    //由于线程是可复用的，在线程执行完毕后要清除上下文属性。
                    RequestContextHolder.resetRequestAttributes();
                }
            };
        });
        //执行初始化
        executor.initialize();
        return executor;
    }

}

